<template>
  <div class="face-queue-container">
    <header class="header">
      <div class="logo-container">
        <img src="../../assets/logo.png" alt="Logo" class="logo">
        <h1>排队叫号系统</h1>
      </div>
      <div class="time">{{ currentTime }}</div>
    </header>

    <div class="content">
      <div class="welcome-section">
        <h2>欢迎使用自助取号系统</h2>
        <p class="subtitle">请选择您要办理的业务类型</p>
      </div>

      <div class="business-section">
        <div
          v-for="business in businessTypes"
          :key="business.id"
          class="business-card"
          @click="selectBusiness(business)"
        >
          <el-icon class="business-icon" :size="50">
            <component :is="getIconByServiceName(business.name)"></component>
          </el-icon>
          <div class="business-info">
            <div class="business-name">{{ business.name }}</div>
            <div class="business-desc">{{ business.description }}</div>
            <div class="queue-info">
              <span class="waiting-count">当前等待: {{ business.currentWaiting }}人</span>
              <span class="estimate-time">预计等待: {{ formatWaitTime(business.avgWaitTime * business.currentWaiting) }}</span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 确认对话框 -->
    <el-dialog
      v-model="dialogVisible"
      title="确认取号"
      width="380px"
      center
    >
      <div class="dialog-content">
        <p>您选择的业务类型: <strong>{{ selectedBusiness?.name }}</strong></p>
        <p>当前等待人数: <strong>{{ selectedBusiness?.currentWaiting }}人</strong></p>
        <p>预计等待时间: <strong>{{ formatWaitTime(selectedBusiness?.averageWaitTime) }}</strong></p>

        <div class="face-verify-section" v-if="isFaceRequired">
          <h3>人脸识别</h3>
          <div class="face-canvas">
            <video ref="videoRef" class="video-stream" autoplay playsinline></video>
            <canvas ref="canvasRef" class="face-canvas" style="display: none;"></canvas>

            <div class="face-overlay" :class="{ 'face-detected': faceDetected }">
              <div class="face-frame"></div>
              <div class="corner top-left"></div>
              <div class="corner top-right"></div>
              <div class="corner bottom-left"></div>
              <div class="corner bottom-right"></div>
            </div>
          </div>

          <div class="face-status">
            {{ faceStatusMessage }}
          </div>
        </div>
      </div>

      <template #footer>
        <div class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="confirmTicket" :disabled="isFaceRequired && !faceDetected">
            确认取号
          </el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 取号成功对话框 -->
    <el-dialog
      v-model="ticketDialogVisible"
      title="取号成功"
      width="380px"
      center
    >
      <div class="ticket-dialog-content">
        <div class="ticket-number">{{ ticketNumber }}</div>
        <div class="ticket-info">
          <p>业务类型: <strong>{{ selectedBusiness?.name }}</strong></p>
          <p>取号时间: <strong>{{ currentTime }}</strong></p>
          <p>等候人数: <strong>{{ selectedBusiness?.currentWaiting }}人</strong></p>
          <p>预计等待: <strong>{{ formatWaitTime(selectedBusiness?.averageWaitTime) }}</strong></p>
        </div>
        <div class="ticket-notice">
          <el-alert
            title="温馨提示"
            type="info"
            description="您的号码将在叫号大屏上显示，请保持关注。被叫号后，请前往窗口进行人脸验证。"
            show-icon
            :closable="false"
          />
        </div>
      </div>

      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="finishTicket">完成</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { ElMessage } from 'element-plus'
import {
  User, Money, CreditCard, Document, List, Setting, Camera,
  Briefcase, OfficeBuilding, Monitor
} from '@element-plus/icons-vue'
import axios from "axios";
import {getToday} from "@/utils/date";

// 当前时间
const currentTime = ref('')
let timer = null

// 业务类型列表 - 模拟数据
let businessTypes = ref([])

let businessTypeMap = new Map;
axios.get("/service-types/getList", {
  params: {
    "pageNum": 1,
    "pageSize": 100,
    "status": 1,
  }
}).then(res => {
  res.data.data.list.forEach(item => {
    businessTypes.value.push(item);
    businessTypeMap.set(item.id, item);
  })
  refreshData();
});


// 对话框控制
const dialogVisible = ref(false)
const ticketDialogVisible = ref(false)
const selectedBusiness = ref(null)
const ticketNumber = ref('')

// 人脸识别相关
const videoRef = ref(null)
const canvasRef = ref(null)
const stream = ref(null)
const faceDetected = ref(false)
const faceStatusMessage = ref('请正视摄像头')
const isFaceRequired = ref(false)

// 更新当前时间
const updateTime = () => {
  const now = new Date()
  const year = now.getFullYear()
  const month = (now.getMonth() + 1).toString().padStart(2, '0')
  const date = now.getDate().toString().padStart(2, '0')
  const hours = now.getHours().toString().padStart(2, '0')
  const minutes = now.getMinutes().toString().padStart(2, '0')
  const seconds = now.getSeconds().toString().padStart(2, '0')
  const day = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][now.getDay()]

  currentTime.value = `${year}年${month}月${date}日 ${hours}:${minutes}:${seconds} ${day}`
}


// 格式化等待时间
const formatWaitTime = (minutes) => {
  if (!minutes) return '请稍后'

  if (minutes < 60) {
    return `约${minutes}分钟`
  } else {
    const hours = Math.floor(minutes / 60)
    const mins = minutes % 60
    return `约${hours}小时${mins > 0 ? `${mins}分钟` : ''}`
  }
}

// 根据业务类型获取图标
const getIconByServiceName = (serviceName) => {
  const iconMap = {
    '个人业务': User,
    '企业业务': OfficeBuilding,
    '信贷业务': Money,
    '理财业务': CreditCard,
    '外汇业务': Document,
    '咨询业务': Monitor
  }

  return iconMap[serviceName] || Briefcase
}

// 选择业务类型
const selectBusiness = (business) => {
  selectedBusiness.value = business
  isFaceRequired.value = business.requireFace
  dialogVisible.value = true

  if (business.requireFace) {
    // 延迟执行，确保对话框已显示
    setTimeout(() => {
      initFaceCamera()
    }, 300)
  }
}

// 初始化人脸摄像头
const initFaceCamera = async () => {
  try {
    // 如果已有流，先关闭
    if (stream.value) {
      stopCamera()
    }

    // 重置人脸检测状态
    faceDetected.value = false
    faceStatusMessage.value = '请正视摄像头...'

    // 获取摄像头流
    stream.value = await navigator.mediaDevices.getUserMedia({
      video: {
        width: 320,
        height: 240,
        facingMode: 'user'
      }
    })

    // 设置视频源
    if (videoRef.value) {
      videoRef.value.srcObject = stream.value
      videoRef.value.onloadedmetadata = () => {
        // 视频准备好后开始处理视频帧
        startFaceDetection()
      }
    }
  } catch (error) {
    console.error('初始化摄像头失败:', error)
    faceStatusMessage.value = '无法访问摄像头，请检查权限设置'
  }
}

// 定时器ID
let faceDetectionTimer = null

// 开始人脸检测
const startFaceDetection = () => {
  // 清除可能存在的旧定时器
  if (faceDetectionTimer) {
    clearInterval(faceDetectionTimer)
  }

  // 设置定时器，每500毫秒处理一次视频帧
  faceDetectionTimer = setInterval(() => {
    processFaceFrame()
  }, 500)
}

// 处理视频帧进行人脸检测
const processFaceFrame = () => {
  if (!videoRef.value || !canvasRef.value || !stream.value) return

  const video = videoRef.value
  const canvas = canvasRef.value
  const context = canvas.getContext('2d')

  // 设置画布尺寸与视频一致
  canvas.width = video.videoWidth
  canvas.height = video.videoHeight

  // 绘制视频帧到画布
  context.drawImage(video, 0, 0, canvas.width, canvas.height)

  // 这里实际项目中应该调用人脸检测API，现在使用模拟数据
  // 在真实环境中，可以将canvas的数据转为blob后发送到后端进行人脸检测

  // 模拟人脸检测过程（随机概率检测到人脸）
  const detected = Math.random() > 0.5

  if (detected && !faceDetected.value) {
    console.log('检测到人脸')
    faceDetected.value = true
    faceStatusMessage.value = '人脸已检测到，请点击确认取号'

    // 真实项目中，这里应该停止连续检测，并保存人脸特征数据
    clearInterval(faceDetectionTimer)
  }
}

// 停止摄像头
const stopCamera = () => {
  // 清除人脸检测定时器
  if (faceDetectionTimer) {
    clearInterval(faceDetectionTimer)
    faceDetectionTimer = null
  }

  // 停止并释放摄像头流
  if (stream.value) {
    stream.value.getTracks().forEach(track => track.stop())
    stream.value = null
  }
}

window.addEventListener('beforeunload', function (event) {
  // 自定义提示信息（部分浏览器会忽略自定义消息）
  event.preventDefault();
  event.returnValue = ''; // 某些浏览器需要设置 returnValue
  socket.close();
});
let sessionId = null;

// 创建 WebSocket 连接
const socket = new WebSocket('ws://127.0.0.1:8080/api/ws/queue-status');

// 监听连接成功事件
socket.addEventListener('open', function (event) {
  console.log('WebSocket 连接已建立');
});

// 监听接收消息事件
socket.addEventListener('message', function (event) {
  console.log('收到消息:', event.data);
  // 如果需要解析 JSON 数据
  try {
    const data = JSON.parse(event.data);
    if (data.type === 'connect') {
      sessionId = data.data;
    } else if (data.type === 'getNumber') {
      ticketNumber.value = data.data.ticketNumber;
    } else if (data.type === 'reFreshData') {
      refreshData();
    }
  } catch (error) {
    console.error('消息解析失败:', error);
  }
});


// 监听错误事件
socket.addEventListener('error', function (event) {
  console.error('WebSocket 错误:', event);
});

// 监听连接关闭事件
socket.addEventListener('close', function (event) {
  console.log('WebSocket 连接已关闭:', event);
});

function apiGetNumber(businessTypeId) {
  axios.post("/queue/getTicket", {
    sessionId: sessionId,
    serviceTypeId: businessTypeId
  }).then(res => {
    if (res.data.code === 200) {
      ElMessage.success("请求票号成功！");
    } else {
      ElMessage.error("请求票号失败！" + res.data.message);
    }
  });
}

function refreshData() {
  axios.post("/queue/reFreshData", {
    "statDate": getToday()
  }).then(res => {
    res.data.data.forEach(item => {
      let business = businessTypeMap.get(item.serviceTypeId)
      console.log(business)
      if (business) {
        business.currentWaiting = item.waitingCount;
        business.avgWaitTime = item.avgWaitTime;
      }
    })
  });
}

// 确认取号
const confirmTicket = () => {
  // 如果需要人脸识别但未检测到人脸，则不处理
  // if (isFaceRequired.value && !faceDetected.value) {
  //   return
  // }
  apiGetNumber(selectedBusiness.value.id);

  // 关闭人脸检测，显示取票成功对话框
  dialogVisible.value = false
  ticketDialogVisible.value = true
  // 在确认取号时自动停止摄像头
  stopCamera()

}

// 完成取号
const finishTicket = () => {
  ticketDialogVisible.value = false
  ElMessage.success(`取号成功，您的号码是${ticketNumber.value}`)
}

// 组件挂载时
onMounted(() => {
  // 更新当前时间
  updateTime()
  timer = setInterval(updateTime, 1000)
})

// 组件卸载时
onUnmounted(() => {
  // 清除定时器
  clearInterval(timer)

  if (faceDetectionTimer) {
    clearInterval(faceDetectionTimer)
  }

  // 确保摄像头已关闭
  stopCamera()
})
</script>

<style scoped>
.face-queue-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: #f6f8fc;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 20px;
  background-color: #1976d2;
  color: white;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

.logo-container {
  display: flex;
  align-items: center;
}

.logo {
  height: 40px;
  margin-right: 15px;
}

.logo-container h1 {
  font-size: 24px;
  font-weight: bold;
}

.time {
  font-size: 18px;
}

.content {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
}

.welcome-section {
  text-align: center;
  margin-bottom: 30px;
}

.welcome-section h2 {
  font-size: 32px;
  color: #1976d2;
  margin-bottom: 10px;
}

.subtitle {
  font-size: 18px;
  color: #616161;
}

.business-section {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
  padding: 0 20px;
}

.business-card {
  background-color: white;
  border-radius: 10px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  display: flex;
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;
}

.business-card:hover {
  transform: translateY(-5px);
  box-shadow: 0 5px 15px rgba(0,0,0,0.15);
}

.business-icon {
  margin-right: 20px;
  color: #1976d2;
}

.business-info {
  flex: 1;
}

.business-name {
  font-size: 20px;
  font-weight: bold;
  margin-bottom: 5px;
  color: #333;
}

.business-desc {
  font-size: 14px;
  color: #666;
  margin-bottom: 15px;
  height: 40px;
}

.queue-info {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  color: #555;
}

.waiting-count, .estimate-time {
  display: inline-block;
  padding: 3px 8px;
  background-color: #e3f2fd;
  border-radius: 4px;
  color: #1976d2;
}

.dialog-content {
  padding: 10px 0;
}

.face-verify-section {
  margin-top: 20px;
  text-align: center;
}

.face-canvas {
  position: relative;
  width: 320px;
  height: 240px;
  margin: 15px auto;
  border-radius: 8px;
  overflow: hidden;
  background-color: #f0f0f0;
}

.video-stream {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.face-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  transition: all 0.3s ease;
}

.face-frame {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 160px;
  height: 160px;
  border-radius: 50%;
  border: 2px dashed rgba(255, 255, 255, 0.5);
}

.face-detected .face-frame {
  border: 2px solid #67C23A;
}

.corner {
  position: absolute;
  width: 20px;
  height: 20px;
  border-color: rgba(255, 255, 255, 0.8);
  border-style: solid;
  border-width: 0;
}

.top-left {
  top: 30px;
  left: 70px;
  border-top-width: 2px;
  border-left-width: 2px;
}

.top-right {
  top: 30px;
  right: 70px;
  border-top-width: 2px;
  border-right-width: 2px;
}

.bottom-left {
  bottom: 30px;
  left: 70px;
  border-bottom-width: 2px;
  border-left-width: 2px;
}

.bottom-right {
  bottom: 30px;
  right: 70px;
  border-bottom-width: 2px;
  border-right-width: 2px;
}

.face-detected .corner {
  border-color: #67C23A;
}

.face-status {
  font-size: 14px;
  color: #606266;
  margin-top: 10px;
  height: 20px;
}

.face-detected ~ .face-status {
  color: #67C23A;
}

.dialog-footer {
  text-align: center;
}

.ticket-dialog-content {
  text-align: center;
}

.ticket-number {
  font-size: 48px;
  font-weight: bold;
  margin: 20px 0;
  color: #f44336;
}

.ticket-info {
  text-align: left;
  margin: 20px 0;
}

.ticket-info p {
  margin: 5px 0;
}

.ticket-notice {
  margin: 20px 0 10px;
}
</style>
